1   /*
2    * Copyright (C) 2006 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.io;
18  
19  import static com.google.common.base.Preconditions.checkNotNull;
20  
21  import java.io.Closeable;
22  import java.io.Flushable;
23  import java.io.IOException;
24  import java.io.Writer;
25  
26  import javax.annotation.Nullable;
27  
28  /**
29   * Writer that places all output on an {@link Appendable} target. If the target
30   * is {@link Flushable} or {@link Closeable}, flush()es and close()s will also
31   * be delegated to the target.
32   *
33   * @author Alan Green
34   * @author Sebastian Kanthak
35   * @since 1.0
36   */
37  class AppendableWriter extends Writer {
38    private final Appendable target;
39    private boolean closed;
40  
41    /**
42     * Creates a new writer that appends everything it writes to {@code target}.
43     *
44     * @param target target to which to append output
45     */
46    AppendableWriter(Appendable target) {
47      this.target = checkNotNull(target);
48    }
49  
50    /*
51     * Abstract methods from Writer
52     */
53  
54    @Override public void write(char cbuf[], int off, int len)
55        throws IOException {
56      checkNotClosed();
57      // It turns out that creating a new String is usually as fast, or faster
58      // than wrapping cbuf in a light-weight CharSequence.
59      target.append(new String(cbuf, off, len));
60    }
61  
62    @Override public void flush() throws IOException {
63      checkNotClosed();
64      if (target instanceof Flushable) {
65        ((Flushable) target).flush();
66      }
67    }
68  
69    @Override public void close() throws IOException {
70      this.closed = true;
71      if (target instanceof Closeable) {
72        ((Closeable) target).close();
73      }
74    }
75  
76    /*
77     * Override a few functions for performance reasons to avoid creating
78     * unnecessary strings.
79     */
80  
81    @Override public void write(int c) throws IOException {
82      checkNotClosed();
83      target.append((char) c);
84    }
85  
86    @Override public void write(@Nullable String str) throws IOException {
87      checkNotClosed();
88      target.append(str);
89    }
90  
91    @Override public void write(@Nullable String str, int off, int len) throws IOException {
92      checkNotClosed();
93      // tricky: append takes start, end pair...
94      target.append(str, off, off + len);
95    }
96  
97    @Override public Writer append(char c) throws IOException {
98      checkNotClosed();
99      target.append(c);
100     return this;
101   }
102 
103   @Override public Writer append(@Nullable CharSequence charSeq) throws IOException {
104     checkNotClosed();
105     target.append(charSeq);
106     return this;
107   }
108 
109   @Override public Writer append(@Nullable CharSequence charSeq, int start, int end)
110       throws IOException {
111     checkNotClosed();
112     target.append(charSeq, start, end);
113     return this;
114   }
115 
116   private void checkNotClosed() throws IOException {
117     if (closed) {
118       throw new IOException("Cannot write to a closed writer.");
119     }
120   }
121 }